package com.awanp3.nimbus.gateway.filter;

import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.httpauth.basic.SaHttpBasicUtil;
import cn.dev33.satoken.reactor.context.SaReactorSyncHolder;
import cn.dev33.satoken.reactor.filter.SaReactorFilter;
import cn.dev33.satoken.router.SaRouter;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.awanp3.nimbus.common.core.constant.HeaderConstants;
import com.awanp3.nimbus.common.core.constant.HttpStatus;
import com.awanp3.nimbus.common.core.constant.SystemConstants;
import com.awanp3.nimbus.common.core.utils.MessageUtils;
import com.awanp3.nimbus.common.core.utils.SpringUtils;
import com.awanp3.nimbus.common.core.utils.StringUtils;
import com.awanp3.nimbus.common.satoken.utils.LoginHelper;
import com.awanp3.nimbus.gateway.config.properties.IgnoreWhiteProperties;
import com.awanp3.nimbus.remote.api.HWorkTokenService;
import com.awanp3.nimbus.system.api.RemoteClientService;
import com.awanp3.nimbus.system.api.RemoteTokenService;
import com.awanp3.nimbus.system.api.domain.vo.RemoteClientVo;
import com.awanp3.nimbus.system.api.domain.vo.RemoteLoginVo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.util.ObjectUtils;

/**
 * [Sa-Token 权限认证] 拦截器
 *
 * @author 20014170
 */
@Slf4j
@RequiredArgsConstructor
@Configuration
public class AuthFilter {

    @DubboReference
    private RemoteClientService remoteClientService;

    @DubboReference
    private HWorkTokenService hWorkTokenService;

    @DubboReference
    private RemoteTokenService remoteTokenService;

    /**
     * 注册 Sa-Token 全局过滤器
     */
    @Bean
    public SaReactorFilter getSaReactorFilter(IgnoreWhiteProperties ignoreWhite) {
        return new SaReactorFilter()
            // 拦截地址
            .addInclude("/**")
            .addExclude("/static/favicon.ico", "/actuator", "/actuator/**", "/resource/sse")
            // 鉴权方法：每次访问进入
            .setAuth(obj -> {
                // 登录校验 -- 拦截所有路由
                SaRouter.match("/**")
                    .notMatch(ignoreWhite.getWhites())
                    .check(r -> {
                        ServerHttpRequest request = SaReactorSyncHolder.getExchange().getRequest();
                        // 检查 header 与 param 里的 XClientId
                        String headerClientId = request.getHeaders().getFirst(HeaderConstants.CLIENT_KEY);
                        String paramClientId = request.getQueryParams().getFirst(HeaderConstants.CLIENT_KEY);
                        String clientId = "";
                        if (StringUtils.isEmpty(headerClientId) && StringUtils.isEmpty(paramClientId)) {
                            throw NotLoginException.newInstance(StpUtil.getLoginType(),
                                "-100", "请求携带的Client Key为空。",
                                StpUtil.getTokenValue());
                        } else if (!StringUtils.isEmpty(headerClientId)) {
                            clientId = headerClientId;
                        } else {
                            clientId = paramClientId;
                        }
                        RemoteClientVo client = remoteClientService.queryByClientId(clientId);
                        if (ObjectUtils.isEmpty(client)) {
                            throw NotLoginException.newInstance(StpUtil.getLoginType(),
                                "-100", "请求携带的Client信息不存在。",
                                StpUtil.getTokenValue());
                        } if (!SystemConstants.NORMAL.equals(client.getStatus())) {
                            throw NotLoginException.newInstance(StpUtil.getLoginType(),
                                "-100", MessageUtils.message("auth.grant.type.blocked"),
                                StpUtil.getTokenValue());
                        }
                        if (client.getGrantType().contains("hWork")){
                            if (!StpUtil.isLogin()) {
                                String hToken = "";
                                String userCode = "";
                                String tenantId = "";
                                String headerHToken = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION);
                                String paramHToken = request.getQueryParams().getFirst(HttpHeaders.AUTHORIZATION);
                                if (StringUtils.isEmpty(headerHToken) && StringUtils.isEmpty(paramHToken)) {
                                    throw NotLoginException.newInstance(StpUtil.getLoginType(),
                                        "-100", "请求携带的Token为空。",
                                        StpUtil.getTokenValue());
                                } else if (!StringUtils.isEmpty(headerHToken)){
                                    hToken = headerHToken;
                                } else if (!StringUtils.isEmpty(paramHToken)){
                                    hToken = paramHToken;
                                }

                                String headerTenantId = request.getHeaders().getFirst(LoginHelper.TENANT_KEY);
                                String paramTenantId = request.getQueryParams().getFirst(LoginHelper.TENANT_KEY);
                                if (StringUtils.isEmpty(headerTenantId) && StringUtils.isEmpty(paramTenantId)) {
                                    throw NotLoginException.newInstance(StpUtil.getLoginType(),
                                        "-100", "请求携带的租户ID为空。",
                                        StpUtil.getTokenValue());
                                } else if (!StringUtils.isEmpty(headerTenantId)){
                                    tenantId = headerTenantId;
                                } else if (!StringUtils.isEmpty(paramTenantId)){
                                    tenantId = paramTenantId;
                                }

                                String analyseResult = hWorkTokenService.analyze(hToken);
                                JSONObject result = JSONUtil.parseObj(analyseResult);
                                if (ObjectUtils.isEmpty(result) || result.getInt("code") != 200) {
                                    throw NotLoginException.newInstance(StpUtil.getLoginType(),
                                        "-100", "解析H-Work Token错误：" + result.getStr("message"),
                                        StpUtil.getTokenValue());
                                }
                                JSONObject user = result.getJSONObject("data");
                                userCode = user.getStr("userCode");

                                JSONObject loginBody = new JSONObject();
                                loginBody.set("tenantId", tenantId);
                                loginBody.set("clientId", clientId);
                                loginBody.set("grantType", "hWork");
                                loginBody.set("username", userCode);
                                loginBody.set("token", hToken);
                                RemoteLoginVo loginVo = remoteTokenService.login(JSONUtil.toJsonStr(loginBody));
                                log.info("登录成功，token: {}", loginVo.toString());
                            }
                        } else {
                            // 检查是否登录 是否有token
                            StpUtil.checkLogin();
                            String currentClientId = StpUtil.getExtra(HeaderConstants.CLIENT_KEY).toString();
                            if (!StringUtils.equals(currentClientId, clientId)) {
                                // token 无效
                                throw NotLoginException.newInstance(StpUtil.getLoginType(),
                                    "-100", "客户端ID与Token不匹配",
                                    StpUtil.getTokenValue());
                            }
                        }
                    });
            }).setError(e -> {
                if (e instanceof NotLoginException) {
                    return SaResult.error(e.getMessage()).setCode(HttpStatus.UNAUTHORIZED);
                }
                return SaResult.error("认证失败，无法访问系统资源").setCode(HttpStatus.UNAUTHORIZED);
            });
    }

    /**
     * 对 actuator 健康检查接口 做账号密码鉴权
     */
    @Bean
    public SaReactorFilter actuatorFilter() {
        String username = SpringUtils.getProperty("spring.security.user.name");
        String password = SpringUtils.getProperty("spring.security.user.password");
        return new SaReactorFilter()
            .addInclude("/actuator", "/actuator/**")
            .addExclude("/actuator/health")
            .setAuth(obj -> {
                SaHttpBasicUtil.check(username + ":" + password);
            })
            .setError(e -> SaResult.error(e.getMessage()).setCode(HttpStatus.UNAUTHORIZED));
    }

}
